import copy
import os
import re
import signal
import traceback
from typing import Dict, List

import toml

from core import utils
from core.data_read import GatewayCase, NodeCase, TestResult, TestCase, TestDevice, ExportSingle, \
    TestDeviceMul, ExportMulticast
from core.serial_port import SerialPort
import time
from api import config, mqtt
from core.utils import PreserveInlineEncoder
from logger.logger import Log
from enum import Enum


#    测试分析模块
class ActionEnum(Enum):
    Ps = 'Performance_Start'
    Pm = 'Performance_Mid'
    Pe = 'Performance_End'


class AutoTest:
    running = False
    case_name = ''
    temp_data = ''
    case_data = {}
    Start_time_map = {}

    # 调用了串口对象的 read 方法来读取数据，并将读取到的数据作为结果返回
    def read_output(self, timeout: int) -> bytes:
        output = self.sp.read(timeout)
        return output

    # 向串口发送命令，读取串口输出，并根据命令是否包含 'Hex' 来决定是否对读取到的数据进行解码并返回字符串结果
    def exec_cmd(self, cmd: str, timeout: int) -> str:
        self.sp.write(cmd)
        output = self.sp.read(timeout)
        if 'Hex' not in cmd:
            return output.decode('utf-8')

    # def read(self, delay):
    #     time.sleep(delay / 1000)
    #     data = b''  # 用于存储读取到的数据
    #     start_time = time.time()  # 记录开始时间
    #
    #     while True:
    #         num = self.p.inWaiting()  # 获取缓冲区中待读取的字节数
    #         if num > 0:
    #             data += self.p.read(num)  # 将数据添加到已读取的数据中
    #         else:
    #             if time.time() - start_time >= (delay / 1000):
    #                 break  # 如果超过延迟时间，退出循环
    #
    #     return data

    # 根据给定的网关命令列表，解析每个命令并返回处理后的命令列表
    # 1、如果元素中包含 'S:'，则使用正则表达式 re.findall(r'(?<=S:).+', GatewayCase.CmdMap[i].replace("\n", ""))[0]
    # 来提取 'S:' 后面的内容，并将其加入 cmd_list 中
    # 2、如果元素中不包含 'S:'，则直接将该元素在 GatewayCase.CmdMap 中对应的值（去掉换行符）加入 cmd_list 中
    def gw_cmd_parse(self, gw_cmd_list) -> list:
        cmd_list = []
        if len(gw_cmd_list) != 0:

            for i in gw_cmd_list:
                if 'S:' in i:
                    cmd_list.append(re.findall(r'(?<=S:).+', GatewayCase.CmdMap[i].replace("\n", ""))[0])
                else:
                    cmd_list.append(GatewayCase.CmdMap[i].replace("\n", ""))
        return cmd_list

    # 是根据给定的节点命令列表，解析每个命令并返回处理后的命令列表
    def node_cmd_parse(self, node_cmd_list) -> list:
        cmd_list = []
        # 判断来检查传入的节点命令列表是否为空
        if len(node_cmd_list) != 0:
            # 提取每个命令中 'S:' 后面的内容，加入 cmd_list
            for i in node_cmd_list:
                cmd_list.append(re.findall(r'(?<=S:).+', NodeCase.CmdMap[i])[0])
        return cmd_list

    def test(self, case: TestCase):
        while True:
            # 网管指令：批量单设备轮询设置开关属性 打开+关闭 是为了单独一个设备的多次开关 || NormalCode是为了其他测试可以正常跳出循环
            if case.cycle == 999999 or case.NormalCode == case.cycle:
                break
            # 创建了变量 case 的一个浅拷贝，并将其赋值给新的变量 case
            copyCase = copy.copy(case)
            AutoTest.case_name = copyCase.name
            AutoTest.running = True
            self.start_time = time.time()
            # 根据给定的网关命令列表，解析每个命令并返回处理后的命令列表
            gw_cmd_list = self.gw_cmd_parse(copyCase.gw_cmd_list)
            # 是根据给定的节点命令列表，解析每个命令并返回处理后的命令列表
            nd_cmd_list = self.node_cmd_parse(copyCase.nd_cmd_list)
            node_dic = {}
            # 网关命令列表不为空
            if len(gw_cmd_list) != 0:
                for msg in gw_cmd_list:
                    # 将配置文件中的NodeList替换excel文件中的@NodeList
                    # 专门为开关单设备多次开关写的判断 SWITCH
                    if "@NodeList" in msg and "@Switch" not in msg:
                        # 读取NodeList
                        node_list = config.Config.arg['TestCase']['Node_List']
                        count = 0
                        for i in node_list:
                            # 替换
                            temp_msg = msg.replace("@NodeList", i)
                            # 如果 AutoTest.Start_time_map 中已经存在键为 i 的项，则会更新该键对应的值为当前的时间戳
                            # 如果 AutoTest.Start_time_map 中不存在键为 i 的项，则会向字典中添加一个新的键值对，键为 i，值为当前的时间戳
                            AutoTest.Start_time_map.update({i: time.time()})
                            # 将配置文件中的Node_MAC_List替换excel文件中的@Node_MAC_List
                            if "@MacList" in temp_msg:
                                # 读取Node_MAC_List
                                node_mac_list = config.Config.arg['TestCase']['Node_MAC_List']
                                # 从第零个Node_MAC_List开始填入
                                temp_msg = temp_msg.replace("@MacList", node_mac_list[count])
                                count += 1
                            # 解析传入的消息内容，提取主题和载荷信息后，通过 MQTT 客户端向指定主题发布消息,('topic:' 和 'payload:' 之间的内容)
                            # 向网关发送命令
                            self.server.send_msg(temp_msg)
                            log = "[Send Msg to Gateway]: {}".format(temp_msg)
                            Log.logger.info(log)
                            time.sleep(copyCase.delay)
                    # 专门为开关单设备多次开关写的判断 SWITCH
                    elif "@NodeList" in msg and "@Switch" in msg:
                        # 读取NodeList
                        node_list = config.Config.arg['TestCase']['Node_List']

                        # 写入toml文件，Switch的值 由case.cycle决定
                        with open('config.toml', 'r', encoding='utf-8') as f:
                            original_data = toml.load(f, _dict=dict)
                        # 添加或更新 TomlSwitch
                        TomlSwitch = {'SWITCH': {'Switch': list({"0", "1"}) * copyCase.cycle}}
                        original_data.update(TomlSwitch)
                        # 将更新后的数据写回到文件中，合并更新内容到原始数据中
                        with open('config.toml', 'w', encoding='utf-8') as f:
                            toml.dump(original_data, f, encoder=PreserveInlineEncoder())

                        switch_list = config.Config.arg['SWITCH']['Switch']
                        count = 0
                        case.fakeCycle = case.cycle
                        for i in node_list:
                            for x in switch_list:
                                # 替换
                                temp_msg = msg.replace("@NodeList", i).replace("@Switch", x)
                                # 如果 AutoTest.Start_time_map 中已经存在键为 i 的项，则会更新该键对应的值为当前的时间戳
                                # 如果 AutoTest.Start_time_map 中不存在键为 i 的项，则会向字典中添加一个新的键值对，键为 i，值为当前的时间戳
                                AutoTest.Start_time_map.update({i: time.time()})
                                # 将配置文件中的Node_MAC_List替换excel文件中的@Node_MAC_List
                                if "@MacList" in temp_msg:
                                    # 读取Node_MAC_List
                                    node_mac_list = config.Config.arg['TestCase']['Node_MAC_List']
                                    # 从第零个Node_MAC_List开始填入
                                    temp_msg = temp_msg.replace("@MacList", node_mac_list[count])
                                    count += 1
                                # 解析传入的消息内容，提取主题和载荷信息后，通过 MQTT 客户端向指定主题发布消息,('topic:' 和 'payload:' 之间的内容)
                                # 向网关发送命令
                                self.server.send_msg(temp_msg)
                                log = "[Send Msg to Gateway]: {}".format(temp_msg)
                                Log.logger.info(log)
                                time.sleep(copyCase.delay)
                                case.cycle = 999999
                    # 没有批量替换的需求，直接发送请求
                    else:
                        self.server.send_msg(msg)
                        time.sleep(copyCase.delay)

            # 节点命令列表不为空
            if len(nd_cmd_list) != 0:
                for cmd in nd_cmd_list:
                    log = "[Send Command]: {}".format(cmd)
                    Log.logger.info(log)
                    # 向串口发送命令，读取串口输出，并根据命令是否包含 'Hex' 来决定是否对读取到的数据进行解码并返回字符串结果
                    readback = self.exec_cmd(cmd, 1000)
                    log = "[Receive Command]: {}".format(readback)
                    Log.logger.info(log)
                    # cmd键 readBack值
                    temp_node = {cmd: readback}
                    node_dic.update(temp_node)
            time.sleep(case.timeout)
            self.timeout = True
            # 更新AutoTest.case_data中AutoTest.case_name键的值为AutoTest.temp_data
            AutoTest.case_data.update({AutoTest.case_name: AutoTest.temp_data})
            AutoTest.temp_data = ''
            AutoTest.running = False
            # user_case_name = mutil为例，将参数case和ActionEnum.Ps = Performance_Start 传给 self.user_case_name函数
            self.operation[self.user_case_name](case, ActionEnum.Ps)
            case.NormalCode += 1

    def test_performance(self, case, style):
        # Performance_Start
        if style == ActionEnum.Ps:
            # case.result_info
            if case.result_info is not None:
                # 从AutoTest.case_data中使用AutoTest.case_name作为键来获取对应的值,将获取的值作为参数传递给case.result_info.is_exist()方法
                res, msg, end_time, temp_error_list = case.result_info.is_exist(
                    AutoTest.case_data[AutoTest.case_name])
                if temp_error_list is not None:
                    self.result.add_error_case({self.count: temp_error_list})
                # 如果有结果 成功次数+1
                if res:
                    self.result.test_pass()
                    self.end_time = utils.timestr_to_timestamp(end_time) if end_time is not None else None
                    if self.end_time is not None:
                        use_time = self.end_time - self.start_time
                        self.result.add_pass_case(use_time)
                # 失败次数 + 1
                else:
                    self.result.test_failed()
                    self.result.failed_list.append(msg)
                    # self.result.add_pass_case({self.count: msg})
                    self.result.add_pass_case(time.time())
                    Log.logger.info(msg)
        # Performance_Mid
        elif style == ActionEnum.Pm:
            log = "[{}] 总计次数: {} 通过次数: {}  不通过次数：{} 报错次数：{} 原始数据： {}  失败数据为{} ".format(
                case.remark,
                self.result.cycle,
                self.result.pass_count, self.result.failed_count, self.result.error_count,
                self.result.list, self.result.failed_list)
            Log.logger.info(log)
            if len(self.result.list) != 0:
                self.huatu(case.name)
            if len(self.result.list) > 0:
                average = sum(self.result.list) / len(self.result.list)
            else:
                average = 0  # 或者根据需要设定一个默认值
            ts = TestDevice(case.name, case.remark, self.result.cycle, self.result.pass_count,
                            str(case.gw_cmd_list), str(case.nd_cmd_list), average,
                            self.start_time,
                            self.end_time, case.result, str(self.result.failed_list), str(self.result.error_list))
            TestResult.resultList.update({case.name: ts})
        # Performance_End
        elif style == ActionEnum.Pe:
            ExportSingle(TestResult.resultList, "res.xlsx")
        else:
            raise "Error ! Action not Found "

    # MULTI 这里已经得到了结果data，先替换config文件再对比
    def test_handle_multi(self, case, style):
        # Performance_Start
        global node
        if style == ActionEnum.Ps:
            if case.result_info is not None:
                self.res = True
                self.list = case.result_info.is_exist_multi()
                data = AutoTest.case_data[AutoTest.case_name]
                Log.logger.info("THE MUTLI DATA:")
                Log.logger.info(data)
                Log.logger.info("THE MUTLI DATA END !!!")
                self.msg = ''

                # 读取config.toml文件，取出Node_List替代@NodeList
                for i in range(0, len(self.list)):
                    temp_data = self.list[i]
                    if case.cycle == 999999:
                        for aaa in range(1, case.fakeCycle + 1):
                            if "@NodeList" in temp_data:
                                self.node_list = config.Config.arg['TestCase']['Node_List']
                                for node in self.node_list:
                                    self.res = True
                                    if len(AutoTest.Start_time_map) > 0:
                                        self.start_time = AutoTest.Start_time_map[node]
                                    temp_data = temp_data.replace("@NodeList", node)
                                    # 如果发现漏掉的node则加入结果列表如
                                    if node not in TestResult.nodeList:
                                        temp_node = TestDeviceMul(case.name, node, self.result.cycle, str(case.gw_cmd_list),
                                                                  case.timeout, 0)
                                        TestResult.nodeList.update({node: temp_node})
                                    # 将temp_data中的特定占位符(包含("$" + i))  替换为config.Config.arg['Device']中对应的值
                                    for i in config.Config.arg['Device']:
                                        if ('$' + i) in temp_data:
                                            temp_data = temp_data.replace('$' + i, str(config.Config.arg['Device'][i]))
                                    # 注释掉的地方
                                    # if temp_data not in data:
                                    #     self.msg += 'data {} Not Found'.format(temp_data)
                                    #     self.res = False

                                    # 第一个出现的方括号到冒号之间的内容
                                    # 匹配temp_data中冒号后面的内容
                                    head = re.findall(r'\[.+?(?=:)', temp_data)[0]
                                    body = re.findall(r'(?<=:).+', temp_data)[0]
                                    Log.logger.info("head:" + head)
                                    Log.logger.info("body:" + body)
                                    # TODO
                                    if head not in data:
                                        self.msg += 'title {} Not Found'.format(head)
                                        self.res = False

                                    if '&' not in body:
                                        if body not in data:
                                            self.msg += 'MSG: {} Not Found ;'.format(body)
                                            self.res = False
                                    else:
                                        # 将 body 字符串按照 & 分隔，提取出每个部分作为一个元素存储在 body_list 列表中
                                        body_list = re.findall('[^&]+?(?=&|$)', body)
                                        for b in body_list:
                                            if b not in data:
                                                self.msg += 'MSG: {} Not Found ;'.format(b)
                                                self.res = False
                                    # 从 data 字符串中提取出位于 36;1m[ 和 ] 之间的子串，并将所有匹配结果存储在 time_list 列表中
                                    time_list = re.findall('(?<=36;1m\[).+?(?=\])', data)
                                    if len(time_list) > 0:
                                        self.time = time_list[-1]
                                    if self.res:
                                        self.end_time = utils.timestr_to_timestamp(
                                            self.time) if self.time is not None else None
                                        if self.end_time is not None:
                                            use_time = self.end_time - self.start_time
                                            TestResult.nodeList[node].cal_time(use_time)
                                            self.result.test_pass()
                                            self.result.times += 1
                                            # SUCCESSFUL NODE DISPLAY
                                            self.result.list.append(node)
                                            temp_data = temp_data.replace(head + body, '', 1)
                                    else:
                                        self.result.test_failed()
                                        self.result.times += 1
                                        # FAILED NODE DISPLAY
                                        self.result.failed_list.append(node)
                                    temp_data = temp_data.replace(node, "@NodeList")

                            elif "@MacList" in temp_data:
                                self.node_list = config.Config.arg['TestCase']['Node_MAC_List']
                                for node in self.node_list:
                                    self.res = True
                                    if len(AutoTest.Start_time_map) > 0:
                                        # self.start_time = AutoTest.Start_time_map[node]
                                        # DUE TO MACLIST,WE GET VALUE = 70B3D50580037E23 BUT  AutoTest.Start_time_map[node]
                                        # THAT'S KEY IS 70B3D50580037E23-1,SO I QUIT TAKE THE TIME
                                        self.start_time = time.time() - time.time()
                                    temp_data = temp_data.replace("@MacList", node)
                                    if node not in TestResult.nodeList:
                                        temp_node = TestDeviceMul(case.name, node, self.result.cycle, str(case.gw_cmd_list),
                                                                  case.timeout, 0)
                                        TestResult.nodeList.update({node: temp_node})
                                    for i in config.Config.arg['Device']:
                                        if ('$' + i) in temp_data:
                                            temp_data = temp_data.replace('$' + i, str(config.Config.arg['Device'][i]))
                                    # if temp_data not in data:
                                    #     self.msg += 'data {} Not Found'.format(temp_data)
                                    #     self.res = False
                                    head = re.findall(r'\[.+?(?=:)', temp_data)[0]
                                    body = re.findall(r'(?<=:).+', temp_data)[0]
                                    Log.logger.info("head:" + head)
                                    Log.logger.info("body:" + body)
                                    # TODO
                                    if head not in data:
                                        self.msg += 'title {} Not Found'.format(head)
                                        self.res = False

                                    if '&' not in body:
                                        if body not in data:
                                            self.msg += 'MSG: {} Not Found ;'.format(body)
                                            self.res = False
                                    else:
                                        body_list = re.findall('[^&]+?(?=&|$)', body)
                                        for b in body_list:
                                            if b not in data:
                                                self.msg += 'MSG: {} Not Found ;'.format(b)
                                                self.res = False
                                    time_list = re.findall('(?<=36;1m\[).+?(?=\])', data)
                                    if len(time_list) > 0:
                                        self.time = time_list[-1]
                                    if self.res:
                                        self.end_time = utils.timestr_to_timestamp(
                                            self.time) if self.time is not None else None
                                        if self.end_time is not None:
                                            use_time = self.end_time - self.start_time
                                            TestResult.nodeList[node].cal_time(use_time)
                                            self.result.test_pass()
                                            self.result.times += 1
                                            self.result.list.append(node)
                                            temp_data = temp_data.replace(head + body, '', 1)
                                    else:
                                        self.result.test_failed()
                                        self.result.times += 1
                                        self.result.failed_list.append(node)
                                    temp_data = temp_data.replace(node, "@MacList")
                    else:
                        if "@NodeList" in temp_data:
                            self.node_list = config.Config.arg['TestCase']['Node_List']
                            for node in self.node_list:
                                self.res = True
                                if len(AutoTest.Start_time_map) > 0:
                                    self.start_time = AutoTest.Start_time_map[node]
                                temp_data = temp_data.replace("@NodeList", node)
                                # 如果发现漏掉的node则加入结果列表如
                                if node not in TestResult.nodeList:
                                    temp_node = TestDeviceMul(case.name, node, self.result.cycle, str(case.gw_cmd_list),
                                                              case.timeout, 0)
                                    TestResult.nodeList.update({node: temp_node})
                                # 将temp_data中的特定占位符(包含("$" + i))  替换为config.Config.arg['Device']中对应的值
                                for i in config.Config.arg['Device']:
                                    if ('$' + i) in temp_data:
                                        temp_data = temp_data.replace('$' + i, str(config.Config.arg['Device'][i]))
                                # 注释掉的地方
                                # if temp_data not in data:
                                #     self.msg += 'data {} Not Found'.format(temp_data)
                                #     self.res = False

                                # 第一个出现的方括号到冒号之间的内容
                                # 匹配temp_data中冒号后面的内容
                                head = re.findall(r'\[.+?(?=:)', temp_data)[0]
                                body = re.findall(r'(?<=:).+', temp_data)[0]
                                Log.logger.info("head:" + head)
                                Log.logger.info("body:" + body)
                                # TODO
                                if head not in data:
                                    self.msg += 'title {} Not Found'.format(head)
                                    self.res = False

                                if '&' not in body:
                                    if body not in data:
                                        self.msg += 'MSG: {} Not Found ;'.format(body)
                                        self.res = False
                                else:
                                    # 将 body 字符串按照 & 分隔，提取出每个部分作为一个元素存储在 body_list 列表中
                                    body_list = re.findall('[^&]+?(?=&|$)', body)
                                    for b in body_list:
                                        if b not in data:
                                            self.msg += 'MSG: {} Not Found ;'.format(b)
                                            self.res = False
                                # 从 data 字符串中提取出位于 36;1m[ 和 ] 之间的子串，并将所有匹配结果存储在 time_list 列表中
                                time_list = re.findall('(?<=36;1m\[).+?(?=\])', data)
                                if len(time_list) > 0:
                                    self.time = time_list[-1]
                                if self.res:
                                    self.end_time = utils.timestr_to_timestamp(
                                        self.time) if self.time is not None else None
                                    if self.end_time is not None:
                                        use_time = self.end_time - self.start_time
                                        TestResult.nodeList[node].cal_time(use_time)
                                        self.result.test_pass()
                                        self.result.times += 1
                                        # SUCCESSFUL NODE DISPLAY
                                        self.result.list.append(node)
                                else:
                                    self.result.test_failed()
                                    self.result.times += 1
                                    # FAILED NODE DISPLAY
                                    self.result.failed_list.append(node)
                                temp_data = temp_data.replace(node, "@NodeList")


                        elif "@MacList" in temp_data:
                            self.node_list = config.Config.arg['TestCase']['Node_MAC_List']
                            for node in self.node_list:
                                self.res = True
                                if len(AutoTest.Start_time_map) > 0:
                                    # self.start_time = AutoTest.Start_time_map[node]
                                    # DUE TO MACLIST,WE GET VALUE = 70B3D50580037E23 BUT  AutoTest.Start_time_map[node]
                                    # THAT'S KEY IS 70B3D50580037E23-1,SO I QUIT TAKE THE TIME
                                    self.start_time = time.time() - time.time()
                                temp_data = temp_data.replace("@MacList", node)
                                if node not in TestResult.nodeList:
                                    temp_node = TestDeviceMul(case.name, node, self.result.cycle, str(case.gw_cmd_list),
                                                              case.timeout, 0)
                                    TestResult.nodeList.update({node: temp_node})
                                for i in config.Config.arg['Device']:
                                    if ('$' + i) in temp_data:
                                        temp_data = temp_data.replace('$' + i, str(config.Config.arg['Device'][i]))
                                # if temp_data not in data:
                                #     self.msg += 'data {} Not Found'.format(temp_data)
                                #     self.res = False
                                head = re.findall(r'\[.+?(?=:)', temp_data)[0]
                                body = re.findall(r'(?<=:).+', temp_data)[0]
                                Log.logger.info("head:" + head)
                                Log.logger.info("body:" + body)
                                # TODO
                                if head not in data:
                                    self.msg += 'title {} Not Found'.format(head)
                                    self.res = False

                                if '&' not in body:
                                    if body not in data:
                                        self.msg += 'MSG: {} Not Found ;'.format(body)
                                        self.res = False
                                else:
                                    body_list = re.findall('[^&]+?(?=&|$)', body)
                                    for b in body_list:
                                        if b not in data:
                                            self.msg += 'MSG: {} Not Found ;'.format(b)
                                            self.res = False
                                time_list = re.findall('(?<=36;1m\[).+?(?=\])', data)
                                if len(time_list) > 0:
                                    self.time = time_list[-1]
                                if self.res:
                                    self.end_time = utils.timestr_to_timestamp(
                                        self.time) if self.time is not None else None
                                    if self.end_time is not None:
                                        use_time = self.end_time - self.start_time
                                        TestResult.nodeList[node].cal_time(use_time)
                                        self.result.test_pass()
                                        self.result.times += 1
                                        self.result.list.append(node)
                                else:
                                    self.result.test_failed()
                                    self.result.times += 1
                                    self.result.failed_list.append(node)
                                temp_data = temp_data.replace(node, "@MacList")

        elif style == ActionEnum.Pm:
            TotalSum = self.result.times
            if TotalSum % 2 == 0:
                TotalSum = TotalSum / 2.0
                self.result.pass_count = self.result.pass_count / 2.0
                self.result.failed_count = self.result.failed_count / 2.0
                self.result.error_count = self.result.error_count / 2.0
            log = "[{}] 总计次数: {} 通过次数: {}  不通过次数：{}  报错次数：{}  原始数据： {}  失败数据为{} ".format(
                case.remark,
                TotalSum,
                self.result.pass_count, self.result.failed_count, self.result.error_count,
                self.result.list, self.result.failed_list)
            Log.logger.info(log)
            # if len(self.result.list) != 0:
            #     self.huatu(case.name)
            # ts = TestDevice(case.name, case.remark, self.result.cycle, self.result.pass_count,
            #                 str(case.gw_cmd_list), str(case.nd_cmd_list), sum(self.result.list) / len(self.result.list),
            #                 self.start_time,
            #                 self.end_time, case.result, str(self.result.failed_list), str(self.result.error_list))
            # TestResult.resultList.update({case.name: ts})
        elif style == ActionEnum.Pe:
            # if config.Config.arg['TestCase']['case'] == "multi":
            if config.Config.arg['TestCase']['case'] == "performance":
                ExportMulticast(TestResult.case, "MulticastResult.xlsx")

        else:
            raise ("Error ! Action not Found ")

    def open_port(self, port: str, bitrate: int):
        if self.sp is None or self.sp.p is None:
            self.sp = SerialPort(port, bitrate)
            self.sp.open()
        elif self.port != port or self.bitrate != bitrate:
            try:
                self.sp.close()
                self.sp = SerialPort(port, bitrate)
                self.sp.open()
            except Exception as e:
                print(e)
                self.sp = None
        self.port = port
        self.bitrate = bitrate

    def start(self, port: str, bitrate: int, ssh):
        if config.Config.arg['TestCase']['type'] == 'mqtt':
            self.server = mqtt.MqttClient()
            utils.run_with_thread(self.server.start)
        else:
            self.server = ssh
        self.open_port(port, bitrate)
        self.port = port
        self.bitrate = bitrate
        time.sleep(5)
        try:
            for i in range(0, self.cycles):
                # 遍历 self.case 字典（或者类中的属性），获取其中的所有键，将每个键依次赋值给变量 key
                for key in self.case.keys():
                    if key is not None:
                        case_list = self.case[key]
                        self.case_list_process(key, case_list)
                self.operation[self.user_case_name]({}, ActionEnum.Pe)
            self.sp.close()
            log = "测试全部结束！！！".center(100, '-')
            pid = os.getpid()  # 获取当前进程的PID
            os.kill(pid, signal.SIGTERM)  # 主动结束指定ID的程序运行

            Log.logger.info(log)


        except Exception as e:
            Log.logger.error("[Test Get Exception] {}".format(e))

    # def case_list_process(self, key, case_list):
    #
    #     try:
    #         case_index = 0
    #         for case in case_list:
    #             # 检查当前测试用例是否需要跳过，如果不需要跳过，则创建一个用于存储测试结果的 TestResult 对象
    #             if case.skip is not True:
    #                 self.result = TestResult(key, case.cycle)
    #                 for t in range(0, case.cycle):
    #                     Log.logger.info(
    #                         "---------------------------------------------------------")
    #                     log = "[Test Case] Name: {} Total: {} Number: {}".format(case.name, case.cycle, t + 1)
    #                     Log.logger.info(log)
    #                     self.count = t + 1
    #                     case_index += 1
    #                     # utils.run_with_thread(self.test(case))
    #                     try:
    #                         utils.run_with_thread(self.test(case))
    #                     except Exception as test_exception:
    #                         # 记录测试异常
    #                         Log.logger.error("[Test Error] Test case '{}' encountered an error: {}".format(case.name,
    #                                                                                                       test_exception))
    #                         continue
    #                     while self.timeout:
    #                         test_result = {case.name: self.res}
    #                         self.timeout = False
    #                 self.result.finish()
    #                 self.operation[self.user_case_name](case, ActionEnum.Pm)
    #                 if config.Config.arg['TestCase']['case'] == "multi":
    #                     TestResult.case.update({case.name: TestResult.nodeList})
    #                     TestResult.nodeList = {}
    #     except Exception as e:
    #         log = "[Test Error] {}".format(e)
    #         Log.logger.error(log)

    # 一个功能模块测完后，在这里检测下一个是否需要跳过
    def case_list_process(self, key, case_list):
        try:
            case_index = 0
            for case in case_list:
                try:  # 在这里添加try-except块
                    # 检查当前测试用例是否需要跳过，如果不需要跳过，则创建一个用于存储测试结果的 TestResult 对象
                    if case.skip is not True:
                        self.result = TestResult(key, case.cycle)
                        for t in range(0, case.cycle):
                            if case.cycle != 999999:
                                Log.logger.info(
                                    "---------------------------------------------------------")
                                log = "[Test Case] Name: {} Total: {} Number: {}".format(case.name, case.cycle, t + 1)
                                Log.logger.info(log)
                            self.count = t + 1
                            case_index += 1
                            try:
                                utils.run_with_thread(self.test(case))
                            except Exception as test_exception:
                                # 记录测试异常
                                Log.logger.error(
                                    "[Test Error] Test case '{}' encountered an error: {}".format(case.name,
                                                                                                  test_exception))
                                traceback.print_exc()
                                continue  # 继续执行下一个循环
                            while self.timeout:
                                test_result = {case.name: self.res}
                                self.timeout = False

                        self.result.finish()
                        self.operation[self.user_case_name](case, ActionEnum.Pm)
                        if config.Config.arg['TestCase']['case'] == "performance":
                            TestResult.case.update({case.name: TestResult.nodeList})
                            TestResult.nodeList = {}
                except Exception as case_exception:
                    # 记录处理单个测试用例异常
                    Log.logger.error(
                        "[Test Error] Encountered an error while processing test case '{}': {}".format(case.name,
                                                                                                       case_exception))
                    traceback.print_exc()
                    continue
        except Exception as e:
            # 记录处理测试用例列表异常
            log = "[Test Error] Encountered an error while processing test case list: {}".format(e)
            Log.logger.error(log)

    def huatu(self, title):
        pass

    def set_case_sample(self, gw: Dict[str, List[TestCase]], node: Dict[str, List[TestCase]],
                        case: Dict[str, List[TestCase]]):
        self.case = case
        self.gw = gw
        self.node = node

    def __init__(self):
        self.res = None
        self.timeout = False
        self.sp = None
        self.case = None
        self.server = None
        self.case_index_map: Dict[str, int] = {}
        self.test_result: Dict[str, TestResult] = {}
        self.result = None
        self.count = None
        self.start_time = None
        self.end_time = None
        self.device = None
        self.list = None
        self.msg = None
        self.cycles = config.Config.arg['TestCase']['cycle']
        self.user_case_name = config.Config.arg['TestCase']['case']
        self.operation = {
            "performance": self.test_handle_multi,
            "common": self.test_performance,
        }
